package com.indusblue.media.video {
	
	import com.indusblue.events.PercentageEvent;
	import com.indusblue.media.video.data.VideoMetaData;
	import com.indusblue.media.video.events.MetaInfoEvent;
	import com.indusblue.media.video.events.NetStreamEvent;
	
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.*;
	import flash.media.Video;
	import flash.net.*;
	
	[Event (name="stop", type="com.indusblue.media.video.events.NetStreamEvent")]
	[Event (name="start", type="com.indusblue.media.video.events.NetStreamEvent")]
	[Event (name="full", type="com.indusblue.media.video.events.NetStreamEvent")]
	[Event (name="flush", type="com.indusblue.media.video.events.NetStreamEvent")]
	[Event (name="empty", type="com.indusblue.media.video.events.NetStreamEvent")]
	[Event (name="metaInfoReady", type="com.indusblue.media.video.events.MetaInfoEvent")]
	[Event (name="loadChange", type="com.indusblue.events.PercentageEvent")]
	
	/**
  	 * Use this for loading and playing back video, most functionality for control and feed-back is available through public methods
  	 * <p>Handles set up for stream, connection and video objects, as well as MetaData feedback
  	 *   
	 * @author ghostmonk 18/11/2008
	 * 
	 */
	public class CoreVideo extends Sprite {
		
		
		
		private var _video:Video;
		private var _stream:NetStream;
		private var _connection:NetConnection;
		private var _duration:Number;
		private var _smoothing:Boolean;
		private var _errorFunction:Function;
		private var _isVideoAdded:Boolean;
		
		
		
		/**
		 * 
		 * @param smoothing set the smoothing property on the scalable video
		 * @param errorFunction function to call if there is an error on the stream... expects an IOErrorEvent
		 * 
		 */
		public function CoreVideo( smoothing:Boolean = false, errorFunction:Function = null ) {
			
			_smoothing = smoothing;
			_errorFunction = errorFunction;
			
			_connection = new NetConnection();
			_connection.connect( null );
			
			var client:Object = new Object();
			client.onMetaData = onMetaData;
			
			_stream = new NetStream( _connection );
				
			_stream.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
			_stream.addEventListener( IOErrorEvent.IO_ERROR, onIOError );
			
			createNewVideo();
			
			_stream.client = client;
			
			_isVideoAdded = false;
		}
		
		
		
		/**
		 * current playback time on the video stream
		 * 
		 * @return 
		 * 
		 */
		public function get currentTime():Number {
			
			return _stream.time;
			
		}
		
		
		
		/**
		 * current playback time as a percent
		 * 
		 * @return 
		 * 
		 */
		public function get currentTimeAsPercent():Number {
			
			return _stream.time / _duration;
			 
		}
		
		
		
		/**
		 * locates the time in the video by a percentage of the entire duration of the video
		 * 
		 * @param percent
		 * 
		 */
		public function set currentTimeAsPercent( percent:Number ):void {
			
			seek( percent*_duration );
			
		} 
		
		
		
		/**
		 * the complete duration of the video in seconds 
		 * 
		 * @return 
		 * 
		 */
		public function get duration():Number {
			
			return _duration;
			
		}
		
		
		
		/**
		 * returns percentage of video loaded
		 * 
		 * @return 
		 * 
		 */
		public function get percentLoaded():Number {
			
			return Math.max( 0, _stream.bytesLoaded/_stream.bytesTotal );
			
		}
		
		
		
		/**
		 * Scales and centers video according to the container
		 * 
		 * @param container and displayObjectContainer the videoScreen is placed into
		 * 
		 */
		public function scaleAndCenterToContainer( container:DisplayObjectContainer ):void {
			
			_video.scaleX = _video.scaleY = 1;
			var ratio:Number = Math.min( container.width/_video.width, container.height/_video.height );
			_video.scaleX = _video.scaleY = ratio;
			_video.x = Math.abs( ( _video.width - container.width ) )/2;
			_video.y = Math.abs( ( _video.height - container.height ) )/2;
			
		}
		
		
		
		/**
		 * scales and positions the video in reference to the stage
		 * 
		 * @param stage
		 * 
		 */
		public function fullStageMode( stage:Stage ):void {
			
			_video.scaleX = _video.scaleY = 1;
			var ratio:Number = Math.min( stage.stageWidth/_video.width, stage.stageHeight/_video.height );
			_video.scaleX = _video.scaleY = ratio;
			_video.x = Math.abs( ( _video.width - stage.stageWidth ) )/2;
			_video.y = Math.abs( ( _video.height - stage.stageHeight ) )/2;
			
		}
		
		
		
		/**
		 * set the width and height of the video object.
		 * 
		 * @param width
		 * @param height
		 * 
		 */
		public function setSize( width:Number, height:Number ):void {
			
			_video.width = width;
			_video.height = height;
			
		}
		
		
		
		/**
		 * To load in a new video asset, due to a bug in the Video.clear() method, a new video object is created each time this function is called
		 * 
		 * @param url net location of the flv
		 * @param autoPlay set it true if you want to video to automatically start playing back
		 * @param getLoadUpdates set to true if you want to listen for loading updates
		 * 
		 */
		public function load( url:String, autoPlay:Boolean = true, getLoadUpdates:Boolean = false ):void {
			
			if( getLoadUpdates ) {
				addEventListener(Event.ENTER_FRAME, onEnterFrame);
			}
			//This is a workaround because Video.clear() does not work. Need to create a new video and delete old one, everytime you
			//wish to play a new video... terrible!!!
			createNewVideo();
			
			_stream.play( url );
			if( !autoPlay ) {
				_stream.seek( 0 );
				_stream.pause();	
			}
			
		}
		
		
		
		/**
		 * close the stream and clean up the event listener
		 * 
		 */
		public function close():void {
			
			removeEventListener( Event.ENTER_FRAME, onEnterFrame );
			_stream.close();
			
		}
		
		
		
		/**
		 * call resume on the stream
		 * 
		 */
		public function play():void {
			
			_stream.resume();
			
		}
		
		
		
		/**
		 * Pause the stream
		 * 
		 */
		public function pause():void {
			
			_stream.pause();
			
		}
		
		
		
		/**
		 * Toggles play back, pause or resume, based on what is currently happening 
		 * 
		 */
		public function togglePlayback():void {
			
			_stream.togglePause();
			
		}
		
		
		
		/**
		 * Jump to the location in seconds... always hit the closest keyframe
		 * 
		 * @param time
		 * 
		 */
		public function seek( time:Number ):void {
			
			_stream.seek( time );
			
		}
		
		
		
		private function createNewVideo():void {
			
			if( _isVideoAdded ) {
				removeChild( _video );
			}
			_video = null;
			_video = new Video();
			_video.smoothing = _smoothing;
			
			_video.attachNetStream( _stream );
			
		}
		
		
		
		private function onNetStatus( e:NetStatusEvent ):void {
			
            dispatchEvent( new NetStreamEvent( e.info.code ) );
            
		}
		
		
		
		private function onMetaData( data:Object ):void {
			
			_duration = data.duration;
			dispatchEvent( new MetaInfoEvent( MetaInfoEvent.META_INFO_READY, new VideoMetaData( data ) ) );
			addChild( _video );
			_isVideoAdded = true;
			
			
		}
		
		
		
		private function onIOError( e:IOErrorEvent ):void {
			
			if( _errorFunction != null ) {
				_errorFunction( e );
			}
			
		}
		
		
		
		private function onEnterFrame( e:Event ):void {
			
			if( percentLoaded >= 1 ) {
				removeEventListener( Event.ENTER_FRAME, onEnterFrame );
			} 
			
			dispatchEvent( new PercentageEvent( PercentageEvent.LOAD_CHANGE, percentLoaded ) );
			
		}
		
		
	}
}